home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 February: Tool Chest / Dev.CD Feb 97 TC.toast / Sample Code / Development Tools & Languages / AppsToGo / DTS.Lib / TreeObj.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-09-22  |  59.0 KB  |  2,374 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:     DTS.Lib
  5. ** File:        TreeObj.c
  6. ** Written by:  Eric Soldan
  7. **
  8. ** Copyright © 1992-1993 Apple Computer, Inc.
  9. ** All rights reserved.
  10. */
  11.  
  12. /* You may incorporate this sample code into your applications without
  13. ** restriction, though the sample code has been provided "AS IS" and the
  14. ** responsibility for its operation is 100% yours.  However, what you are
  15. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  16. ** after having made changes. If you're going to re-distribute the source,
  17. ** we require that you make it clear in the source that the code was
  18. ** descended from Apple Sample Code, but that you've made changes. */
  19.  
  20. #include "DTS.Lib2.h"
  21. #include "DTS.Lib.protos.h"
  22.  
  23. #ifndef __FILES__
  24. #include <Files.h>
  25. #endif
  26.  
  27. #ifndef __TREEOBJ__
  28. #include "TreeObj.h"
  29. #endif
  30.  
  31.  
  32.  
  33. #define NEW_CHILD     1
  34. #define DISPOSE_CHILD 2
  35. #define MOVE_CHILD    3
  36. #define SWAP_CHILDREN 4
  37. #define CHANGE_CHILD  5
  38.  
  39.  
  40.  
  41. extern TreeObjProcPtr    gTreeObjMethods[];
  42. extern long                gMinTreeObjSize[];
  43.  
  44. static TreeObjHndl    CopyOneChild(TreeObjHndl chndl, TreeObjHndl copyToHndl, short copyCNum);
  45. static OSErr        ReadBranch(TreeObjHndl hndl, short fileRefNum);
  46. static OSErr        ReadTreeObjHeader(TreeObjHndl hndl, short fileRefNum);
  47. static OSErr        WriteTreeObjHeader(TreeObjHndl hndl, short fileRefNum);
  48.  
  49. static void            DoNumberTree0(TreeObjHndl hndl);
  50. static TreeObjHndl    GetUndoTaskHndl(TreeObjHndl undo, short editType);
  51. static TreeObjHndl    NewUndoPart(TreeObjHndl taskHndl, short action, TreeObjHndl shndl, short cnum, TreeObjHndl dhndl, short dcnum, Boolean deepCopy);
  52.  
  53. static OSErr        PostNewChild(short editType, TreeObjHndl phndl, short cnum);
  54. static OSErr        PostDisposeChild(short editType, TreeObjHndl phndl, short cnum);
  55. static void            PostMoveChild(short editType, TreeObjHndl shndl, short scnum, TreeObjHndl dhndl, short dcnum);
  56. static void            PostSwapChildren(short editType, TreeObjHndl hndla, short cnuma, TreeObjHndl hndlb, short cnumb);
  57.  
  58. static void            UndoNewChild(TreeObjHndl undoPart);
  59. static void            UndoDisposeChild(TreeObjHndl undoPart);
  60. static void            UndoMoveChild(TreeObjHndl undoPart);
  61. static void            UndoModifyChild(TreeObjHndl undoPart);
  62. static void            UndoModifyChildren(TreeObjHndl dhndl, TreeObjHndl uhndl, Boolean deepCopy);
  63. static void            UndoSwapChildren(TreeObjHndl undoPart);
  64.  
  65.  
  66.  
  67. /**********************************************************************/
  68. /**********************************************************************/
  69.  
  70. #ifdef applec
  71. #pragma segment ATGTreeObj
  72. #endif
  73.  
  74. /**********************************************************************/
  75. /**********************************************************************/
  76.  
  77.  
  78.  
  79. /* Creates an object with no parent.  This object will be the root object
  80. ** for a tree.  Returns nil upon failure. */
  81.  
  82. TreeObjHndl    NewRootObj(short ctype, long size)
  83. {
  84.     TreeObjHndl        root;
  85.     TreeObjPtr        ptr;
  86.     char            *cptr;
  87.     short            i;
  88.     TreeObjProcPtr    proc;
  89.  
  90.     if (size < gMinTreeObjSize[ctype])
  91.         size = gMinTreeObjSize[ctype];
  92.             /* Ensure a minimally-sized object. */
  93.  
  94.     root = (TreeObjHndl)NewHandle(sizeof(TreeObj) + size);
  95.     if (!root) return(nil);
  96.         /* Oh well...
  97.         ** Note that since we are creating an orphan, it isn't possible to purge
  98.         ** old undos to possibly make space.  This means that for anyone calling
  99.         ** NewRootObj to get a handlewho considers it okay to purge old undos to
  100.         ** get the memory if necessary, has to do the following:
  101.         ** 1)  Call NewRootObj
  102.         ** 2)  If failure, call PurgeUndo for a particular document to purge the
  103.         **     oldest undo for that document to free up ram.
  104.         ** 3)  If PurgeUndo returns true, then something was purged and there is more
  105.         **     ram available.  Loop back to step 1 and try to create the object again.
  106.         ** 4)  If PurgeUndo returns false, then there really isn't any more ram.  One
  107.         **     possibility is to try purging undos from other documents, but this is
  108.         **     probably rude. */
  109.  
  110.     cptr = GetDataPtr(root);
  111.     for (i = 0; i < size; ++i) cptr[i] = 0;
  112.         /* Initialize data area to 0's.  This is very nice of us. */
  113.  
  114.     ptr = *root;                     /* Deref root object once.      */
  115.     ptr->type        = ctype;         /* Flag what object type it is. */
  116.     ptr->numChildren = 0;            /* Give root 0 children.        */
  117.     ptr->dataSize    = size;        /* Remember the data area size. */
  118.     ptr->treeID      = 0;            /* Requisite do-nothing field.  */
  119.     ptr->parent      = nil;            /* We aren't owned yet.         */
  120.  
  121.     proc = gTreeObjMethods[ctype];
  122.     if (proc) {                                            /* If this object type has a proc...   */
  123.         if ((*proc)(root, INITMESSAGE, CREATEINIT)) {    /* Call the proc with an init message. */
  124.             DisposeHandle((Handle)root);                /* If the init complains, it's no go.  */
  125.             return(nil);
  126.         }
  127.     }
  128.  
  129.     return(root);    /* Success.  Return the handle. */
  130. }
  131.  
  132.  
  133.  
  134. /**********************************************************************/
  135.  
  136.  
  137.  
  138. /* Creates a child of the specified type and adds it to the parent at the
  139. ** specified location.  Returns nil upon failure. */
  140.  
  141. TreeObjHndl    NewChild(short editType, TreeObjHndl phndl, short cnum, short ctype, long size)
  142. {
  143.     TreeObjHndl        chndl;
  144.     TreeObjProcPtr    proc;
  145.  
  146.     for (;;) {
  147.         chndl = NewRootObj(ctype, size);    /* Let somebody else do the creative work. */
  148.         if (chndl) break;                    /* We succeeded at creating an orphan. */
  149.         if (!PurgeUndo(phndl)) return(nil);
  150.             /* Oh well...
  151.             ** There is really no memory.  We even purged the undos to try to make
  152.             ** room, but we still didn't have enough ram to create the orphan object. */
  153.     };
  154.  
  155.     /* Now that we have successfully created an orphan object, make it someone's child. */
  156.  
  157.     GetChildHndlPtr(phndl, &cnum, 1);
  158.         /* Adjust cnum to within bounds.  See GetChildHndlPtr() for more info. */
  159.  
  160.     if (InsertChildHndl(phndl, chndl, cnum)) {
  161.             /* Couldn't insert the child into the parent handle table. */
  162.         proc = gTreeObjMethods[ctype];
  163.         if (proc)
  164.             (*proc)(chndl, FREEMESSAGE, 0);
  165.                 /* Since we couldn't attatch, call the object with a free message so that
  166.                 ** it can get rid of any additional memory that was allocated when an
  167.                 ** init message was sent to it. */
  168.         DisposeHandle((Handle)chndl);
  169.             /* After the free message, the object now consists of a single handle.
  170.             ** Dispose this handle and it is history completely. */
  171.         return(nil);
  172.     }
  173.  
  174.     if (PostNewChild(editType, phndl, cnum)) {
  175.         DisposeChild(NO_EDIT, phndl, cnum);
  176.         chndl = nil;
  177.     }
  178.  
  179.     DoTreeObjMethod(chndl, UNDOMESSAGE, UNDOTODOC);
  180.     return(chndl);
  181. }
  182.  
  183.  
  184.  
  185. /**********************************************************************/
  186.  
  187.  
  188.  
  189. /* Disposes of the specified child and all offspring of that child. */
  190.  
  191. void    DisposeChild(short editType, TreeObjHndl phndl, short cnum)
  192. {
  193.     TreeObjHndl    chndl;
  194.     static long    longZero;        /* For 68000 lame long addressing. */
  195.  
  196.     if (GetChildHndlPtr(phndl, &cnum, 0)) {
  197.         /* This test checks that there are children in the table, plus it
  198.         ** also adjusts cnum to legit values, if possible. */
  199.  
  200.         chndl = GetChildHndl(phndl, cnum);
  201.  
  202.         if (editType) {
  203.             if (PostDisposeChild(editType, phndl, cnum)) {
  204.                     /* Posting a DisposeChild can actually take more ram.
  205.                     ** The reason for this is that the object isn't disposed of.
  206.                     ** It is moved into the undo, plus information as to where to
  207.                     ** put it back is kept.  If PostDisposeChild fails, then we
  208.                     ** are in a bad way memory-wise, so we really need to let the
  209.                     ** dispose occur.  To accomplish this, we disable undos for
  210.                     ** this document, and then we go ahead and allow a straight
  211.                     ** dispose of the child.  This will get the job done, plus it
  212.                     ** will free up some ram.
  213.                     ** The reason that undos are disabled is that the edit that
  214.                     ** is occuring may have multiple operations.  We want to stop
  215.                     ** collection of the remaining operations for this edit, as
  216.                     ** it wouldn't be a complete undo anyway.  The undos will
  217.                     ** be enabled again when NewUndo is called to start a new edit. */
  218.                 DisableUndo(phndl);
  219.                     /* Undo collection off temporarily, plus all undos are purged,
  220.                     ** thus freeing up ram. */
  221.                 editType = 0;
  222.             }
  223.         }
  224.  
  225.         if (!editType) {
  226.             DoTreeObjMethod(chndl, UNDOMESSAGE, UNDOFROMDOC);
  227.                 /* If the dispose was successfully posted, then the object got a message that
  228.                 ** it was leaving the document via PostDisposeChild calling MoveChild.  The
  229.                 ** only way that PostDisposeChild can fail (and therefore not call MoveChild)
  230.                 ** is if an undoTask couldn't be created, and this is prior to MoveChild being
  231.                 ** called.  What all this means is that if we are here, the object hadn't yet
  232.                 ** received a message about leaving the document. */
  233.             DisposeObjAndOffspring(GetChildHndl(phndl, cnum));
  234.             BlockMove(&longZero, GetChildHndlPtr(phndl, &cnum, 0), sizeof(long));
  235.                 /* Since the child no longer exists, DeleteChildHndl can't set
  236.                 ** the parent reference for the child to nil.  Setting the reference
  237.                 ** to the child prevents DeleteChildHndl from doing this. */
  238.             DeleteChildHndl(phndl, cnum);
  239.         }
  240.     }
  241. }
  242.  
  243.  
  244.  
  245. /**********************************************************************/
  246.  
  247.  
  248.  
  249. /* Copies the specified child (and all offspring of that child if deepCopy is true). */
  250.  
  251. TreeObjHndl    CopyChild(short editType, TreeObjHndl shndl, short scnum,
  252.                       TreeObjHndl dhndl, short dcnum, Boolean deepCopy)
  253. {
  254.     TreeObjHndl    chndl, copyHndl;
  255.  
  256.     if (!GetChildHndlPtr(shndl, &scnum, 0)) return(nil);
  257.         /* Adjust scnum to within bounds, if possible. */
  258.  
  259.     GetChildHndlPtr(dhndl, &dcnum, 1);
  260.         /* Adjust dcnum to within bounds. */
  261.  
  262.     copyHndl = CopyOneChild(chndl = GetChildHndl(shndl, scnum), dhndl, dcnum);
  263.     if (!copyHndl) return(nil);
  264.  
  265.     if (deepCopy) {
  266.         if (CopyChildren(chndl, copyHndl)) {        /* Copy child's children. */
  267.             DisposeChild(NO_EDIT, dhndl, dcnum);
  268.             return(nil);
  269.         }
  270.     }
  271.  
  272.     if (PostNewChild(editType, dhndl, dcnum)) {
  273.         DisposeChild(NO_EDIT, dhndl, dcnum);
  274.         return(nil);
  275.     }
  276.  
  277.     DoTreeObjMethod(copyHndl, UNDOMESSAGE, UNDOTODOC);
  278.     return(copyHndl);
  279. }
  280.  
  281.  
  282.  
  283. /**********************************************************************/
  284.  
  285.  
  286.  
  287. /* Moves a child from one location on the tree to another. */
  288.  
  289. OSErr    MoveChild(short editType, TreeObjHndl shndl, short scnum, TreeObjHndl dhndl, short dcnum)
  290. {
  291.     TreeObjHndl    chndl;
  292.  
  293.     if (!GetChildHndlPtr(shndl, &scnum, 0)) return(paramErr);
  294.         /* Adjust scnum to within bounds.  Adjustment not possible if no children. */
  295.  
  296.     GetChildHndlPtr(dhndl, &dcnum, 1);        /* Adjust dcnum to within bounds. */
  297.  
  298.     chndl = GetChildHndl(shndl, scnum);
  299.     DoTreeObjMethod(chndl, UNDOMESSAGE, UNDOFROMDOC);
  300.     DeleteChildHndl(shndl, scnum);
  301.  
  302.     GetChildHndlPtr(dhndl, &dcnum, 1);
  303.         /* Adjust dcnum to within bounds.  It may be just out of bounds if shndl
  304.         ** and dhndl are the same.  After the DeleteChildHndl, dcnum may be 2 past
  305.         ** the end, instead of the allowable 1 for a target. */
  306.  
  307.     InsertChildHndl(dhndl, chndl, dcnum);
  308.     DoTreeObjMethod(chndl, UNDOMESSAGE, UNDOTODOC);
  309.  
  310.     PostMoveChild(editType, shndl, scnum, dhndl, dcnum);
  311.     return(noErr);
  312. }
  313.  
  314.  
  315.  
  316. /**********************************************************************/
  317.  
  318.  
  319.  
  320. /* Saves a copy of the child in the undo hierarchy for undo/redo purposes. */
  321.  
  322. OSErr    ModifyChild(short editType, TreeObjHndl phndl, short cnum, Boolean deepCopy)
  323. {
  324.     TreeObjHndl    undo, task, part, copy;
  325.     short        i;
  326.     OSErr        err;
  327.  
  328.     if (!editType) return(noErr);
  329.  
  330.     err  = noErr;
  331.     copy = nil;
  332.     undo = GetUndoHndl(phndl);
  333.  
  334.     if (mDerefUndo(undo)->disabled)
  335.         copy = phndl;        /* Used for flag purposes. */
  336.  
  337.     else {
  338.         task = GetUndoTaskHndl(undo, editType);
  339.         if (task) {
  340.             for (i = (*task)->numChildren; i;) {
  341.                 part = GetChildHndl(task, --i);
  342.                 if (mDerefUndoPart(part)->actionType == CHANGE_CHILD)
  343.                     if ((mDerefUndoPart(part)->shndl ==phndl) && (mDerefUndoPart(part)->scnum ==cnum))
  344.                         return(noErr);
  345.                             /* Child is already posted as changed for this undo editType. */
  346.             }
  347.  
  348.             part = NewUndoPart(task, CHANGE_CHILD, phndl, cnum, nil, 0, deepCopy);
  349.             if (part)
  350.                 copy = CopyChild(NO_EDIT, phndl, cnum, part, -1, deepCopy);
  351.         }
  352.     }
  353.  
  354.     if (!copy) {
  355.         DisableUndo(phndl);
  356.         err = memFullErr;
  357.     }
  358.  
  359.     return(err);
  360. }
  361.  
  362.  
  363.  
  364. /**********************************************************************/
  365.  
  366.  
  367.  
  368. /* Swaps the child handles. */
  369.  
  370. OSErr    SwapChildren(short editType, TreeObjHndl hndla, short cnuma, TreeObjHndl hndlb, short cnumb)
  371. {
  372.     TreeObjHndl    *tptra, *tptrb, hndl;
  373.  
  374.     if (!(tptra = GetChildHndlPtr(hndla, &cnuma, 0))) return(paramErr);
  375.     if (!(tptrb = GetChildHndlPtr(hndlb, &cnumb, 0))) return(paramErr);
  376.  
  377.     BlockMove(tptra, &hndl, sizeof(TreeObjHndl));
  378.     BlockMove(tptrb, tptra, sizeof(TreeObjHndl));
  379.     BlockMove(&hndl, tptra, sizeof(TreeObjHndl));
  380.  
  381.     (*GetChildHndl(hndla, cnuma))->parent = hndla;
  382.     (*GetChildHndl(hndlb, cnumb))->parent = hndlb;
  383.  
  384.     PostSwapChildren(editType, hndla, cnuma, hndlb, cnumb);
  385.     return(noErr);
  386. }
  387.  
  388.  
  389.  
  390. /**********************************************************************/
  391.  
  392.  
  393.  
  394. /* Swaps the child data without swapping the handles. */
  395.  
  396. OSErr    SwapTreeObjData(TreeObjHndl hndla, TreeObjHndl hndlb)
  397. {
  398.     TreeObjHndl    hndl;
  399.     long        size, sizea, sizeb, i;
  400.     char        *ptra, *ptrb, c;
  401.     OSErr        err;
  402.  
  403.     sizea = (*hndla)->dataSize;
  404.     sizeb = (*hndlb)->dataSize;
  405.     if (sizea > sizeb) {        /* Make hndla the small one and hndlb the big one. */
  406.         size  = sizea;
  407.         sizea = sizeb;
  408.         sizeb = size;
  409.         hndl  = hndla;
  410.         hndla = hndlb;
  411.         hndlb = hndl;
  412.     }
  413.  
  414.     err = SetDataSize(hndla, sizeb);        /* Make the small one big. */
  415.     if (!err) {
  416.         ptra = GetDataPtr(hndla);
  417.         ptrb = GetDataPtr(hndlb);
  418.         for (i = 0; i < sizeb; ++i) {
  419.             c = ptra[i];
  420.             ptra[i] = ptrb[i];
  421.             ptrb[i] = c;
  422.         }
  423.         SetDataSize(hndlb, sizea);            /* Make the big one small. */
  424.     }
  425.  
  426.     return(err);
  427. }
  428.  
  429.  
  430.  
  431. /**********************************************************************/
  432. /**********************************************************************/
  433.  
  434.  
  435.  
  436. /* This disposes of the object and any offspring of that object.  It does not remove
  437. ** the object from the parent's child handle table. */
  438.  
  439. void    DisposeObjAndOffspring(TreeObjHndl ohndl)
  440. {
  441.     short            nc;
  442.     TreeObjProcPtr    proc;
  443.  
  444.     if (!ohndl) return;
  445.  
  446.     proc = gTreeObjMethods[(*ohndl)->type];
  447.     if (proc)
  448.         (*proc)(ohndl, FREEMESSAGE, 0);
  449.             /* If the object has any additional deallocation to do, let it do it. */
  450.  
  451.     while ((nc = (*ohndl)->numChildren) != 0) {
  452.         DisposeObjAndOffspring(GetChildHndl(ohndl, nc - 1));
  453.         (*ohndl)->numChildren--;
  454.     }
  455.     DisposeHandle((Handle)ohndl);
  456. }
  457.  
  458.  
  459.  
  460. /**********************************************************************/
  461.  
  462.  
  463.  
  464. /* Copies the children (and children below that and so on) of one object to
  465. ** another object.  Used internally by CopyChild for deep copies. */
  466.  
  467. OSErr    CopyChildren(TreeObjHndl shndl, TreeObjHndl dhndl)
  468. {
  469.     TreeObjHndl    chndl, copyHndl;
  470.     short    i;
  471.     OSErr    err;
  472.  
  473.     for (i = (*shndl)->numChildren; i;) {
  474.         chndl    = GetChildHndl(shndl, --i);
  475.         copyHndl = CopyOneChild(chndl, dhndl, 0);
  476.         if (!copyHndl) return(memFullErr);
  477.         err = CopyChildren(chndl, copyHndl);
  478.         if (err) return(err);
  479.     }
  480.     return(noErr);
  481. }
  482.  
  483.  
  484.  
  485. /**********************************************************************/
  486.  
  487.  
  488.  
  489. /* Used internally by CopyChild for shallow copies. */
  490.  
  491. static TreeObjHndl    CopyOneChild(TreeObjHndl chndl, TreeObjHndl copyToHndl, short copycnum)
  492. {
  493.     TreeObjHndl        copyHndl;
  494.     short            type;
  495.     long            size;
  496.     TreeObjProcPtr    proc;
  497.  
  498.     type = (*chndl)->type;
  499.     size = (*chndl)->dataSize;
  500.  
  501.  
  502.     proc = gTreeObjMethods[type];    /* Prevent NewChild() from calling the */
  503.     gTreeObjMethods[type] = nil;    /* object for INITMESSAGE. */
  504.  
  505.     copyHndl = NewChild(NO_EDIT, copyToHndl, copycnum, type, size);
  506.         /* NewChild takes care of bounds-checking for copycnum.  The child data has
  507.         ** not been fully initialized, as we prevented the INITMESSAGE. */
  508.  
  509.     gTreeObjMethods[type] = proc;    /* Re-enable messaging the object. */
  510.  
  511.     if (!copyHndl) return(nil);
  512.  
  513.     BlockMove(GetDataPtr(chndl), GetDataPtr(copyHndl), size);
  514.     if (proc) {
  515.         if ((*proc)(copyHndl, COPYMESSAGE, (long)chndl)) {
  516.             DisposeChild(NO_EDIT, copyHndl, copycnum);
  517.             return(nil);
  518.         }
  519.     }
  520.  
  521.     return(copyHndl);
  522. }
  523.  
  524.  
  525.  
  526. /**********************************************************************/
  527. /**********************************************************************/
  528.  
  529.  
  530.  
  531. /* Adds an existing child to a parent's child handle table. */
  532.  
  533. OSErr    InsertChildHndl(TreeObjHndl phndl, TreeObjHndl chndl, short cnum)
  534. {
  535.     TreeObjHndl    *tptr;
  536.     TreeObjPtr    ptr;
  537.     long        oldSize, newSize, dhSize;
  538.     long        oldTblSize, tblOffset;
  539.     OSErr        err;
  540.  
  541.     oldSize = GetHandleSize((Handle)phndl);
  542.     dhSize  = sizeof(TreeObj) + (ptr = *phndl)->dataSize;        /* Data + header size. */
  543.  
  544.     oldTblSize = ptr->numChildren * sizeof(TreeObjHndl);
  545.  
  546.     newSize = dhSize + oldTblSize + sizeof(TreeObjHndl);
  547.     newSize |= 0x1F;
  548.     ++newSize;
  549.  
  550.     if (newSize > oldSize) {
  551.         SetHandleSize((Handle)phndl, newSize);
  552.         err = MemError();
  553.         if (err) return(err);
  554.         ptr = *phndl;
  555.     }
  556.  
  557.     tptr      = GetChildHndlPtr(phndl, &cnum, 1);
  558.     tblOffset = cnum * sizeof(TreeObjHndl);
  559.     BlockMove((char *)tptr, (char *)(tptr + 1), oldTblSize - tblOffset);
  560.  
  561.     ptr->numChildren++;
  562.     BlockMove(&chndl, tptr, sizeof(TreeObjHndl));
  563.     (*chndl)->parent = phndl;
  564.     return(noErr);
  565. }
  566.  
  567.  
  568.  
  569. /**********************************************************************/
  570.  
  571.  
  572.  
  573. /* Removes a child from the parent's child handle table. */
  574.  
  575. void    DeleteChildHndl(TreeObjHndl phndl, short cnum)
  576. {
  577.     TreeObjHndl    *tptr;
  578.     TreeObjHndl    chndl;
  579.     TreeObjPtr    ptr;
  580.     long        dhSize, tblSize, tblOffset;
  581.  
  582.     if (!(tptr = GetChildHndlPtr(phndl, &cnum, 0))) return;
  583.     BlockMove(tptr, &chndl, sizeof(TreeObjHndl));
  584.  
  585.     dhSize    = sizeof(TreeObj) + (ptr = *phndl)->dataSize;
  586.     tblSize   = ptr->numChildren * sizeof(TreeObjHndl);
  587.     tblOffset = cnum * sizeof(TreeObjHndl);
  588.  
  589.     BlockMove((char *)(tptr + 1), (char *)tptr, tblSize - tblOffset - sizeof(TreeObjHndl));
  590.     SetHandleSize((Handle)phndl, ((dhSize + tblSize - sizeof(TreeObjHndl)) | 0x1F) + 1);
  591.     (*phndl)->numChildren--;
  592.  
  593.     if (chndl)
  594.         (*chndl)->parent = nil;
  595. }
  596.  
  597.  
  598.  
  599. /**********************************************************************/
  600.  
  601.  
  602.  
  603. /* Given an object handle, return the root handle. */
  604.  
  605. TreeObjHndl    GetRootHndl(TreeObjHndl hndl)
  606. {
  607.     for (; (*hndl)->parent; hndl = (*hndl)->parent) {};
  608.     return(hndl);
  609. }
  610.  
  611.  
  612.  
  613. /**********************************************************************/
  614.  
  615.  
  616.  
  617. /* Given a parent handle and a child number, this returns the child handle. */
  618.  
  619. TreeObjHndl    GetChildHndl(TreeObjHndl phndl, short cnum)
  620. {
  621.     TreeObjHndl    *tptr, th;
  622.  
  623.     if (!(tptr = GetChildHndlPtr(phndl, &cnum, 0))) return(nil);
  624.     BlockMove(tptr, &th, sizeof(TreeObjHndl));
  625.     return(th);
  626. }
  627.  
  628.  
  629.  
  630. /**********************************************************************/
  631.  
  632.  
  633.  
  634. /* Return a pointer into the child handle table.  This also validates (and corrects)
  635. ** cnum so that it is in range, if possible.  Depending on the usage, pointing to
  636. ** just after the child handle table is either valid or invalid.  If a handle is
  637. ** being added to the table, then pointing just after the table is valid.  If a
  638. ** handle is being removed or referenced, then pointing just after the table is
  639. ** invalid.  the parameter endCase determines which case we are dealing with.  If
  640. ** endCase is 0, then pointing just after the child handle table is invalid, and
  641. ** if the cnum value passed in causes this, then nil is returned for the pointer.
  642. ** if endCase is 1, then pointing just after the child handle table is okay, and
  643. ** therefore nil will never be returned as the pointer.  Any cnum value out of
  644. ** range will be corrected (if possible) to be within range. */
  645.  
  646. TreeObjHndl    *GetChildHndlPtr(TreeObjHndl phndl, short *cnum, short endCase)
  647. {
  648.     TreeObjPtr    ptr;
  649.     short        nc;
  650.     long        dhSize, tblOffset;
  651.  
  652.     ptr = *phndl;
  653.     if (!((nc = ptr->numChildren) + endCase)) return(nil);
  654.  
  655.     if ((*cnum < 0) || (*cnum >= nc + endCase))
  656.         *cnum = nc - 1 + endCase;
  657.  
  658.     dhSize    = sizeof(TreeObj) + ptr->dataSize;
  659.     tblOffset = *cnum * sizeof(TreeObjHndl);
  660.     return((TreeObjHndl *)((char *)ptr + dhSize + tblOffset));
  661. }
  662.  
  663.  
  664.  
  665. /**********************************************************************/
  666.  
  667.  
  668.  
  669. /* Given a child handle, this returns the child number of that
  670. ** handle in the parent's child handle table. */
  671.  
  672. short    GetChildNum(TreeObjHndl hndl)
  673. {
  674.     TreeObjHndl    phndl, *tptr, th;
  675.     short        nc, j;
  676.  
  677.     if (!(phndl = (*hndl)->parent)) return(-1);
  678.         /* Child doesn't have a parent, and therefore it is a root. */
  679.  
  680.     j = 0;
  681.     tptr = GetChildHndlPtr(phndl, &j, 0);
  682.     if (tptr) {
  683.         nc  = (*phndl)->numChildren;
  684.         for (; j < nc; ++j) {
  685.             BlockMove(tptr++, &th, sizeof(TreeObjHndl));
  686.             if (th == hndl) return(j);
  687.         }
  688.     }
  689.  
  690.     return(-1);
  691. }
  692.  
  693.  
  694.  
  695. /**********************************************************************/
  696. /**********************************************************************/
  697.  
  698.  
  699.  
  700. /* Adjusts the data size, either greater or smaller, depending on the sign of 'delta'. */
  701.  
  702. OSErr    AdjustDataSize(TreeObjHndl hndl, long delta)
  703. {
  704.     TreeObjPtr    ptr;
  705.     long        dhSize, tblSize;
  706.     char        *cptr;
  707.     OSErr        err;
  708.  
  709.     ptr = *hndl;
  710.     tblSize = ptr->numChildren * sizeof (TreeObjHndl);
  711.     dhSize  = sizeof(TreeObj) + ptr->dataSize;
  712.  
  713.     if (!(delta & 0x80000000L)) {
  714.         SetHandleSize((Handle)hndl, ((dhSize + tblSize + delta) | 0x1F) + 1);
  715.         err = MemError();
  716.         if (err) return(err);
  717.         ptr = *hndl;
  718.     }
  719.  
  720.     cptr = (char *)ptr + dhSize;
  721.     BlockMove(cptr, cptr + delta, tblSize);
  722.     ptr->dataSize += delta;
  723.  
  724.     if (delta & 0x80000000L)
  725.         SetHandleSize((Handle)hndl, ((dhSize + tblSize + delta) | 0x1F) + 1);
  726.  
  727.     return(noErr);
  728. }
  729.  
  730.  
  731.  
  732. /**********************************************************************/
  733.  
  734.  
  735.  
  736. /* Sets the data size to newSize. */
  737.  
  738. OSErr    SetDataSize(TreeObjHndl hndl, long newSize)
  739. {
  740.     return(AdjustDataSize(hndl, newSize - (*hndl)->dataSize));
  741. }
  742.  
  743.  
  744.  
  745. /**********************************************************************/
  746.  
  747.  
  748.  
  749. /* Slides some of the data in the data portion of the object starting at
  750. ** 'offset' by a 'delta' amount, either forward or backward, depending
  751. ** on the sign of 'delta'. */
  752.  
  753. OSErr    SlideData(TreeObjHndl hndl, long offset, long delta)
  754. {
  755.     long    dataSize;
  756.     char    *cptr;
  757.     OSErr    err;
  758.  
  759.     dataSize = (*hndl)->dataSize;
  760.  
  761.     if (!(delta & 0x80000000L)) {
  762.         err = AdjustDataSize(hndl, delta);
  763.         if (err) return(err);
  764.     }
  765.  
  766.     cptr = (char *)GetDataPtr(hndl) + offset;
  767.     BlockMove(cptr, cptr + delta, dataSize - offset);
  768.  
  769.     if (delta & 0x80000000L)
  770.         AdjustDataSize(hndl, delta);
  771.  
  772.     return(noErr);
  773. }
  774.  
  775.  
  776.  
  777. /**********************************************************************/
  778.  
  779.  
  780.  
  781. /* Returns a pointer to the beginning of the object's data area.
  782. ** THIS DOES NOT LOCK THE HANDLE!!  The pointer may become invalid
  783. ** if memory moves. */
  784.  
  785. void    *GetDataPtr(TreeObjHndl hndl)
  786. {
  787.     return(*hndl + 1);
  788. }
  789.  
  790.  
  791.  
  792. /**********************************************************************/
  793. /**********************************************************************/
  794.  
  795.  
  796.  
  797. /* Given an open file that has been previously written via WriteTree, this function
  798. ** is called to read the file data into ram.  The root object for the file is already
  799. ** created by InitDocument.  The file must be open, and the file position must be
  800. ** set to the byte location where the root object of the file starts.  Once this is
  801. ** so, just call this function with a reference to the root object and the open file
  802. ** refrence number. */
  803.  
  804. OSErr    ReadTree(TreeObjHndl hndl, short fileRefNum)
  805. {
  806.     OSErr    err;
  807.  
  808.     err = ReadBranch(hndl, fileRefNum);
  809.     if (err) {
  810.         while ((*hndl)->numChildren)
  811.             DisposeChild(NO_EDIT, hndl, 0);
  812.     }
  813.     else {
  814.         DoNumberTree(hndl);
  815.         DoFTreeMethod(hndl, CONVERTMESSAGE, CONVERTTOHNDL);
  816.     }
  817.  
  818.     return(err);
  819. }
  820.  
  821.  
  822.  
  823. /**********************************************************************/
  824.  
  825.  
  826.  
  827. /* This is an internal function for recursively reading the data from the
  828. ** open file. */
  829.  
  830. static OSErr    ReadBranch(TreeObjHndl hndl, short fileRefNum)
  831. {
  832.     TreeObjHndl        chndl;
  833.     TreeObjProcPtr    proc;
  834.     short            numChildren, cnum;
  835.     OSErr            err;
  836.  
  837.     err = ReadTreeObjHeader(hndl, fileRefNum);
  838.     if (err) {
  839.         (*hndl)->numChildren = 0;    /* So we can dispose the portion that was read. */
  840.         return(err);
  841.     }
  842.  
  843.     numChildren = (*hndl)->numChildren;    /* So we can dispose of a partial read if */
  844.     (*hndl)->numChildren = 0;            /* we have a failure after this point.    */
  845.  
  846.     proc = gTreeObjMethods[(*hndl)->type];
  847.     if (proc)
  848.         err = (*proc)(hndl, FREADMESSAGE, fileRefNum);
  849.     else
  850.         err = ReadTreeObjData(hndl, fileRefNum);
  851.  
  852.     if (err) return(err);
  853.  
  854.     for (cnum = 0; cnum < numChildren; ++cnum) {
  855.         if (!(chndl = NewRootObj(EMPTYOBJ, 0))) return(memFullErr);
  856.         if (InsertChildHndl(hndl, chndl, cnum)) {
  857.             DisposeObjAndOffspring(chndl);
  858.             return(memFullErr);
  859.         }
  860.         err = ReadBranch(chndl, fileRefNum);
  861.         if (err) return(err);
  862.     }
  863.  
  864.     return(noErr);
  865. }
  866.  
  867.  
  868.  
  869. /**********************************************************************/
  870.  
  871.  
  872.  
  873. static OSErr    ReadTreeObjHeader(TreeObjHndl hndl, short fileRefNum)
  874. {
  875.     TreeObj        header;
  876.     TreeObjHndl    parent;
  877.     OSErr        err;
  878.     long        count;
  879.  
  880.     count = sizeof(TreeObj) - sizeof(TreeObjHndl);
  881.     if (!(err = FSRead(fileRefNum, &count, &header))) {
  882.         parent = (*hndl)->parent;
  883.         **hndl = header;
  884.         (*hndl)->parent = parent;
  885.     }
  886.  
  887.     return(err);
  888. }
  889.  
  890.  
  891.  
  892. /**********************************************************************/
  893.  
  894.  
  895.  
  896. /* Read in dataSize number of bytes into the object. */
  897.  
  898. OSErr    ReadTreeObjData(TreeObjHndl hndl, short fileRefNum)
  899. {
  900.     long    dataSize;
  901.     OSErr    err;
  902.     char    hstate;
  903.     Ptr        dataPtr;
  904.  
  905.     if (!(err = SetDataSize(hndl, dataSize = (*hndl)->dataSize))) {
  906.         hstate = HGetState((Handle)hndl);
  907.         HLock((Handle)hndl);
  908.         dataPtr = GetDataPtr(hndl);
  909.         err     = FSRead(fileRefNum, &dataSize, dataPtr);
  910.         HSetState((Handle)hndl, hstate);
  911.     }
  912.  
  913.     return(err);
  914. }
  915.  
  916.  
  917.  
  918. /**********************************************************************/
  919.  
  920.  
  921.  
  922. /* Given an open file that is ready to be written to, this function is called to
  923. ** write the file tree to the designated file. */
  924.  
  925. OSErr    WriteTree(TreeObjHndl hndl, short fileRefNum)
  926. {
  927.     TreeObjProcPtr    proc;
  928.     short            cnum;
  929.     OSErr            err;
  930.  
  931.     err = WriteTreeObjHeader(hndl, fileRefNum);
  932.     if (!err) {
  933.  
  934.         DoTreeObjMethod(hndl, CONVERTMESSAGE, CONVERTTOID);
  935.             /* Ready data to be written to file.  Any references to handles are invalid
  936.             ** when written to disk.  These need to be converted to a reference that
  937.             ** makes sense when read in from disk when a file is opened.  The standard
  938.             ** way to do this is to convert the handle reference to a tree-obj-number
  939.             ** reference.  Prior to WriteTree() being called, DoNumberTree() is called.
  940.             ** DoNumberTree() walks the tree and numbers each handle sequentially, thus
  941.             ** giving each handle a unique id number. */
  942.  
  943.         proc = gTreeObjMethods[(*hndl)->type];
  944.         if (proc)
  945.             err = (*proc)(hndl, FWRITEMESSAGE, fileRefNum);
  946.         else
  947.             err = WriteTreeObjData(hndl, fileRefNum);
  948.  
  949.         DoTreeObjMethod(hndl, CONVERTMESSAGE, CONVERTTOHNDL);
  950.             /* Undo any id references back to handle references. */
  951.     }
  952.  
  953.  
  954.     if (!err) {
  955.         for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum) {
  956.             err = WriteTree(GetChildHndl(hndl, cnum), fileRefNum);
  957.             if (err) break;
  958.         }
  959.     }
  960.  
  961.     return(err);
  962. }
  963.  
  964.  
  965.  
  966. /**********************************************************************/
  967.  
  968.  
  969.  
  970. static OSErr    WriteTreeObjHeader(TreeObjHndl hndl, short fileRefNum)
  971. {
  972.     TreeObj    header;
  973.     OSErr    err;
  974.     long    count;
  975.  
  976.     header = **hndl;
  977.     count = sizeof(TreeObj) - sizeof(TreeObjHndl);
  978.     err = FSWrite(fileRefNum, &count, &header);
  979.  
  980.     return(err);
  981. }
  982.  
  983.  
  984.  
  985. /**********************************************************************/
  986.  
  987.  
  988.  
  989. /* Write out dataSize number of bytes from the object. */
  990.  
  991. OSErr    WriteTreeObjData(TreeObjHndl hndl, short fileRefNum)
  992. {
  993.     long    dataSize;
  994.     OSErr    err;
  995.     char    hstate;
  996.     Ptr        dataPtr;
  997.  
  998.     dataSize = (*hndl)->dataSize;
  999.  
  1000.     hstate  = HGetState((Handle)hndl);
  1001.     HLock((Handle)hndl);
  1002.     dataPtr = GetDataPtr(hndl);
  1003.     err     = FSWrite(fileRefNum, &dataSize, dataPtr);
  1004.     HSetState((Handle)hndl, hstate);
  1005.  
  1006.     return(err);
  1007. }
  1008.  
  1009.  
  1010.  
  1011. /**********************************************************************/
  1012.  
  1013.  
  1014.  
  1015. /* Call the object for each member of the tree (or branch) starting
  1016. ** from the back of the tree working forward. */
  1017.  
  1018. void    DoBTreeMethod(TreeObjHndl hndl, short message, long data)
  1019. {
  1020.     short    cnum;
  1021.  
  1022.     DoTreeObjMethod(hndl, message, data);
  1023.  
  1024.     for (cnum = (*hndl)->numChildren; cnum;)
  1025.         DoBTreeMethod(GetChildHndl(hndl, --cnum), message, data);
  1026. }
  1027.  
  1028.  
  1029.  
  1030. /**********************************************************************/
  1031.  
  1032.  
  1033.  
  1034. /* Same as DoBTreeMethod, except that an error aborts tree walk and returns error. */
  1035.  
  1036. OSErr    DoErrBTreeMethod(TreeObjHndl hndl, short message, long data)
  1037. {
  1038.     short    cnum;
  1039.     OSErr    err;
  1040.  
  1041.     err = DoTreeObjMethod(hndl, message, data);
  1042.     if (err) return(err);
  1043.  
  1044.     for (cnum = (*hndl)->numChildren; cnum;) {
  1045.         err = DoErrBTreeMethod(GetChildHndl(hndl, --cnum), message, data);
  1046.         if (err) return(err);
  1047.     }
  1048.  
  1049.     return(noErr);
  1050. }
  1051.  
  1052.  
  1053.  
  1054. /**********************************************************************/
  1055.  
  1056.  
  1057.  
  1058. /* Call the object for each member of the tree (or branch) starting
  1059. ** from the front of the tree working backward. */
  1060.  
  1061. void    DoFTreeMethod(TreeObjHndl hndl, short message, long data)
  1062. {
  1063.     short    cnum;
  1064.  
  1065.     DoTreeObjMethod(hndl, message, data);
  1066.  
  1067.     for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum) 
  1068.         DoFTreeMethod(GetChildHndl(hndl, cnum), message, data);
  1069. }
  1070.  
  1071.  
  1072.  
  1073. /**********************************************************************/
  1074.  
  1075.  
  1076.  
  1077. /* Same as DoFTreeMethod, except that an error aborts tree walk and returns error. */
  1078.  
  1079. OSErr    DoErrFTreeMethod(TreeObjHndl hndl, short message, long data)
  1080. {
  1081.     short    cnum;
  1082.     OSErr    err;
  1083.  
  1084.     err = DoTreeObjMethod(hndl, message, data);
  1085.     if (err) return(err);
  1086.  
  1087.     for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum) {
  1088.         err = DoErrFTreeMethod(GetChildHndl(hndl, cnum), message, data);
  1089.         if (err) return(err);
  1090.     }
  1091.  
  1092.     return(noErr);
  1093. }
  1094.  
  1095.  
  1096.  
  1097. /**********************************************************************/
  1098.  
  1099.  
  1100.  
  1101. /* If the object has a method procedure, call it.  If no method procedure,
  1102. ** then do nothing. */
  1103.  
  1104. long    DoTreeObjMethod(TreeObjHndl hndl, short message, long data)
  1105. {
  1106.     TreeObjProcPtr    proc;
  1107.  
  1108.     proc = gTreeObjMethods[(*hndl)->type];
  1109.     if (proc) return((*proc)(hndl, message, data));
  1110.     return(0);
  1111. }
  1112.  
  1113.  
  1114.  
  1115. /**********************************************************************/
  1116.  
  1117.  
  1118.  
  1119. /* Number each member in the tree with a unique treeID.  The tree is number
  1120. ** sequentially from front to back.  The first treeID number is 1.  0 is
  1121. ** reserved for Hndl2ID/ID2Hndl conversions where it is possible that the
  1122. ** handle value is nil.  The nil handle will convert to 0, and convert back
  1123. ** to nil. */
  1124.  
  1125. void        DoNumberTree(TreeObjHndl hndl)
  1126. {
  1127.     DoNumberTree0(GetRootHndl(hndl));
  1128. }
  1129.  
  1130.  
  1131.  
  1132. /**********************************************************************/
  1133.  
  1134.  
  1135.  
  1136. static void    DoNumberTree0(TreeObjHndl hndl)
  1137. {
  1138.     short            cnum;
  1139.     static short    nodeNum;
  1140.  
  1141.     if (!(*hndl)->parent)
  1142.         nodeNum = 0;
  1143.     (*hndl)->treeID = ++nodeNum;
  1144.  
  1145.     for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum)
  1146.         DoNumberTree0(GetChildHndl(hndl, cnum));
  1147. }
  1148.  
  1149.  
  1150.  
  1151. /**********************************************************************/
  1152.  
  1153.  
  1154.  
  1155. /* This function is used to convert a handle reference into a treeID reference.
  1156. ** A pointer to the handle reference is passed in.  Typical usage will be where
  1157. ** a handle object has a reference to another handle object.  Handle object
  1158. ** references aren't meaningful when saved to disk, and therefore don't persist
  1159. ** in their native form.  These handle references need to be converted into
  1160. ** a treeID reference so that they can be saved.
  1161. ** The tree first needs to be numbered so that the treeID references are unique
  1162. ** and meaningful.  The tree is numbered by first calling DoNumberTree().  It
  1163. ** numbers all the members of the tree hierarchy uniquely and sequentially. */
  1164.  
  1165. void    Hndl2ID(TreeObjHndl *hndl)
  1166. {
  1167.     if (*hndl)
  1168.         *hndl = (TreeObjHndl)(**hndl)->treeID;
  1169. }
  1170.  
  1171.  
  1172.  
  1173. /**********************************************************************/
  1174.  
  1175.  
  1176.  
  1177. /* Given a tree object ID and a reference object (any member of the tree),
  1178. ** return the cooresponding object handle.  DoNumberTree() must be called
  1179. ** prior to using this function, and after the last change to the tree, as
  1180. ** it generates the object treeID numbers for the entire tree. */
  1181.  
  1182. void    ID2Hndl(TreeObjHndl refHndl, TreeObjHndl *hndl)
  1183. {
  1184.     short        cnum;
  1185.     TreeObjHndl    chndl;
  1186.  
  1187.     if ((!refHndl) || (!*hndl)) return;
  1188.  
  1189.     refHndl = GetRootHndl(refHndl);
  1190.     for (;;) {
  1191.         if ((*refHndl)->treeID == (long)*hndl) {
  1192.             *hndl = refHndl;
  1193.             return;
  1194.         }
  1195.         if (!(cnum = (*refHndl)->numChildren)) return;
  1196.         for (; cnum;) {
  1197.             chndl = GetChildHndl(refHndl, --cnum);
  1198.             if ((*chndl)->treeID <= (long)*hndl) {
  1199.                 refHndl = chndl;
  1200.                 break;
  1201.             }
  1202.         }
  1203.     }
  1204. }
  1205.  
  1206.  
  1207.  
  1208. /**********************************************************************/
  1209.  
  1210.  
  1211.  
  1212. /* Given an object handle, return the undo handle. */
  1213.  
  1214. TreeObjHndl    GetUndoHndl(TreeObjHndl undo)
  1215. {
  1216.     for (; (*undo)->parent; undo = (*undo)->parent) {};
  1217.     if ((*undo)->type == ROOTOBJ)
  1218.         undo = mDerefRoot(undo)->undo;
  1219.     return(undo);
  1220. }
  1221.  
  1222.  
  1223.  
  1224. /**********************************************************************/
  1225.  
  1226.  
  1227.  
  1228. /* Used to close out an old undo task.  Closing out the old task means that
  1229. ** any editing to the document will be recorded into a new undo task.  Use
  1230. ** this to separate two edits of the same edit type that would otherwise
  1231. ** get recorded into the same undo task. */
  1232.  
  1233. void    NewUndo(TreeObjHndl hndl)
  1234. {
  1235.     TreeObjHndl    undo;
  1236.  
  1237.     undo = GetUndoHndl(hndl);
  1238.     mDerefUndo(undo)->lastEditType = NO_EDIT;
  1239.     mDerefUndo(undo)->disabled     = false;
  1240. }
  1241.  
  1242.  
  1243.  
  1244. /**********************************************************************/
  1245.  
  1246.  
  1247.  
  1248. /* If an edit fails, it can be backed out of by calling this.  All edits
  1249. ** that were done will be undone, thus recovering the state of the
  1250. ** document prior to the edit. */
  1251.  
  1252. void    RevertEdit(TreeObjHndl hndl, Boolean fixup)
  1253. {
  1254.     TreeObjHndl    root;
  1255.     FileRecHndl    frHndl;
  1256.     Boolean        docDirty;
  1257.  
  1258.     root     = GetRootHndl(hndl);
  1259.     frHndl   = mDerefRoot(root)->frHndl;
  1260.     docDirty = (*frHndl)->fileState.docDirty;
  1261.  
  1262.     DoUndoTask(hndl, DOUNDO, fixup);
  1263.         /* Use the undo mechanism to back out of the edit task. */
  1264.  
  1265.     DisposeChild(NO_EDIT, GetUndoHndl(hndl), -1);
  1266.         /* Get rid of the undo that was just used to revert.
  1267.         ** Leave the rest of the undos. */
  1268.  
  1269.     (*frHndl)->fileState.docDirty = docDirty;
  1270. }
  1271.  
  1272.  
  1273.  
  1274. /**********************************************************************/
  1275.  
  1276.  
  1277.  
  1278. /* Call this to undo or redo editing to the document.  If redo is false,
  1279. ** the it is an undo task. */
  1280.  
  1281. void    DoUndoTask(TreeObjHndl hndl, Boolean redo, Boolean fixup)
  1282. {
  1283.     FileRecHndl    frHndl;
  1284.     TreeObjHndl    undo, undoTask, undoPart;
  1285.     short        numUndos, undoDepth, undoPartNum;
  1286.     short        beg, end, inc;
  1287.     Point        contOrg;
  1288.  
  1289.     NewUndo(undo = GetUndoHndl(hndl));
  1290.  
  1291.     numUndos  = (*undo)->numChildren;
  1292.     undoDepth = mDerefUndo(undo)->undoDepth;
  1293.  
  1294.     if ((redo) && (numUndos == undoDepth)) return;
  1295.     if (!(numUndos | undoDepth)) return;
  1296.  
  1297.     frHndl = mDerefUndo(undo)->frHndl;
  1298.  
  1299.     if (redo) undoDepth++;
  1300.     undoTask = GetChildHndl(undo, --undoDepth);
  1301.  
  1302.     if (!redo) {
  1303.         GetContentOrigin((*frHndl)->fileState.window, &contOrg);
  1304.         mDerefUndoTask(undoTask)->redoOrigin = contOrg;
  1305.         contOrg = mDerefUndoTask(undoTask)->undoOrigin;
  1306.     }
  1307.     else contOrg = mDerefUndoTask(undoTask)->redoOrigin;
  1308.  
  1309.     if (fixup)
  1310.         DoUndoFixup(frHndl, contOrg, 0);
  1311.             /* Prepare for undo task, such as deselecting the current selection so
  1312.             ** that the undone stuff can be displayed as the only selected stuff. */
  1313.  
  1314.     beg = (*undoTask)->numChildren - 1;
  1315.     end = -1;
  1316.     inc = -1;
  1317.     if (redo) {
  1318.         end = ++beg;
  1319.         beg = 0;
  1320.         inc = 1;
  1321.     }
  1322.  
  1323.     for (undoPartNum = beg; undoPartNum != end; undoPartNum += inc) {
  1324.         undoPart = GetChildHndl(undoTask, undoPartNum);
  1325.         switch(mDerefUndoPart(undoPart)->actionType) {
  1326.             case NEW_CHILD:
  1327.                 UndoNewChild(undoPart);
  1328.                 break;
  1329.             case DISPOSE_CHILD:
  1330.                 UndoDisposeChild(undoPart);
  1331.                 break;
  1332.             case MOVE_CHILD:
  1333.                 UndoMoveChild(undoPart);
  1334.                 break;
  1335.             case CHANGE_CHILD:
  1336.                 UndoModifyChild(undoPart);
  1337.                 break;
  1338.             case SWAP_CHILDREN:
  1339.                 UndoSwapChildren(undoPart);
  1340.                 break;
  1341.         }
  1342.     }
  1343.  
  1344.     inc = -1;
  1345.     if (redo)
  1346.         inc = 1;
  1347.  
  1348.     mDerefUndo(undo)->undoDepth += inc;
  1349.  
  1350.     if (fixup)
  1351.         DoUndoFixup(frHndl, contOrg, 1);
  1352.             /* Clean up and redisplay after undo task. */
  1353.  
  1354.     SetDocDirty(frHndl);
  1355. }
  1356.  
  1357.  
  1358.  
  1359. /**********************************************************************/
  1360.  
  1361.  
  1362.  
  1363. static TreeObjHndl    GetUndoTaskHndl(TreeObjHndl undo, short editType)
  1364. {
  1365.     TreeObjHndl        lastTaskHndl;
  1366.     short            lastEditType, undoDepth, addNewUndo, numUndoLevels, maxNumUndos;
  1367.     Point            contOrg;
  1368.     FileRecHndl        frHndl;
  1369.     WindowPtr        window;
  1370.  
  1371.     if (!(maxNumUndos = mDerefUndo(undo)->maxNumUndos)) return(nil);
  1372.  
  1373.     lastEditType = mDerefUndo(undo)->lastEditType;
  1374.     undoDepth    = mDerefUndo(undo)->undoDepth;
  1375.  
  1376.     addNewUndo = false;
  1377.     if (editType != lastEditType)
  1378.         addNewUndo = true;
  1379.     if (!(numUndoLevels = (*undo)->numChildren))
  1380.         addNewUndo = true;
  1381.  
  1382.     while (undoDepth < numUndoLevels) {
  1383.         DisposeChild(NO_EDIT, undo, --numUndoLevels);
  1384.         addNewUndo = true;        /* Flushing old also indicates a new undo. */
  1385.     }                            /* undoDepth now is the same as numUndoLevels. */
  1386.  
  1387.     lastTaskHndl = nil;
  1388.     if (!addNewUndo) {
  1389.         lastTaskHndl = GetChildHndl(undo, -1);        /* Get last child handle. */
  1390.         if ((editType) && (editType != mDerefUndoTask(lastTaskHndl)->editType))
  1391.             lastTaskHndl = nil;
  1392.     }
  1393.  
  1394.     if (!lastTaskHndl) {
  1395.         while (numUndoLevels >= maxNumUndos) {
  1396.             DisposeChild(NO_EDIT, undo, 0);
  1397.             mDerefUndo(undo)->undoDepth--;
  1398.             numUndoLevels--;
  1399.         }    /* Restrict number of undos to designated level. */
  1400.  
  1401.         lastTaskHndl = NewChild(NO_EDIT, undo, numUndoLevels, UNDOTASKOBJ, 0);
  1402.         if (lastTaskHndl) {
  1403.             mDerefUndo(undo)->lastEditType = editType;
  1404.             mDerefUndo(undo)->undoDepth++;
  1405.             frHndl = mDerefUndo(undo)->frHndl;
  1406.             window = (*frHndl)->fileState.window;
  1407.             GetContentOrigin(window, &contOrg);
  1408.             mDerefUndoTask(lastTaskHndl)->editType   = editType;
  1409.             mDerefUndoTask(lastTaskHndl)->undoOrigin = contOrg;
  1410.             mDerefUndoTask(lastTaskHndl)->redoOrigin = contOrg;
  1411.         }
  1412.     }
  1413.  
  1414.     return(lastTaskHndl);
  1415. }
  1416.  
  1417.  
  1418.  
  1419. /**********************************************************************/
  1420.  
  1421.  
  1422.  
  1423. static TreeObjHndl    NewUndoPart(TreeObjHndl taskHndl, short action, TreeObjHndl shndl, short scnum,
  1424.                                 TreeObjHndl dhndl, short dcnum, Boolean deepCopy)
  1425. {
  1426.     TreeObjHndl    partHndl;
  1427.     UndoPartObj    *partPtr;
  1428.  
  1429.     partHndl = NewChild(NO_EDIT, taskHndl, -1, UNDOPARTOBJ, 0);    /* Add new child to end. */
  1430.     if (partHndl) {
  1431.         partPtr = GetDataPtr(partHndl);
  1432.         partPtr->actionType = action;
  1433.         partPtr->shndl      = shndl;
  1434.         partPtr->scnum      = scnum;
  1435.         partPtr->dhndl      = dhndl;
  1436.         partPtr->dcnum      = dcnum;
  1437.         partPtr->deepCopy   = deepCopy;
  1438.     }
  1439.  
  1440.     return(partHndl);
  1441. }
  1442.  
  1443.  
  1444.  
  1445. /**********************************************************************/
  1446.  
  1447.  
  1448.  
  1449. static OSErr    PostNewChild(short editType, TreeObjHndl phndl, short cnum)
  1450. {
  1451.     TreeObjHndl    undo, task;
  1452.  
  1453.     if (!editType) return(noErr);
  1454.  
  1455.     undo = GetUndoHndl(phndl);
  1456.     if (mDerefUndo(undo)->disabled) return(noErr);
  1457.  
  1458.     task = GetUndoTaskHndl(undo, editType);
  1459.     if (task)
  1460.         if (NewUndoPart(task, NEW_CHILD, phndl, cnum, nil, 0, false)) return(noErr);
  1461.  
  1462.     return(memFullErr);
  1463. }
  1464.  
  1465.  
  1466.  
  1467. /**********************************************************************/
  1468.  
  1469.  
  1470.  
  1471. static OSErr    PostDisposeChild(short editType, TreeObjHndl phndl, short cnum)
  1472. {
  1473.     TreeObjHndl    undo, task, part;
  1474.  
  1475.     if (!editType) return(noErr);
  1476.  
  1477.     undo = GetUndoHndl(phndl);
  1478.     if (mDerefUndo(undo)->disabled) return(noErr);
  1479.  
  1480.     task = GetUndoTaskHndl(undo, editType);
  1481.     if (task) {
  1482.         part = NewUndoPart(task, DISPOSE_CHILD, phndl, cnum, nil, 0, false);
  1483.         if (part) {
  1484.             MoveChild(NO_EDIT, phndl, cnum, part, -1);
  1485.             return(noErr);
  1486.         }
  1487.     }
  1488.  
  1489.     return(memFullErr);
  1490. }
  1491.  
  1492.  
  1493.  
  1494. /**********************************************************************/
  1495.  
  1496.  
  1497.  
  1498. static void    PostMoveChild(short editType, TreeObjHndl shndl, short scnum, TreeObjHndl dhndl, short dcnum)
  1499. {
  1500.     TreeObjHndl    undo, task;
  1501.  
  1502.     if (editType) {
  1503.         undo = GetUndoHndl(shndl);
  1504.         if (!mDerefUndo(undo)->disabled) {
  1505.             task = GetUndoTaskHndl(undo, editType);
  1506.             if (task) {
  1507.                 NewUndoPart(task, MOVE_CHILD, shndl, scnum, dhndl, dcnum, false);
  1508.             }
  1509.         }
  1510.     }
  1511. }
  1512.  
  1513.  
  1514.  
  1515. /**********************************************************************/
  1516.  
  1517.  
  1518.  
  1519. static void    PostSwapChildren(short editType, TreeObjHndl hndla, short cnuma, TreeObjHndl hndlb, short cnumb)
  1520. {
  1521.     TreeObjHndl    undo, task;
  1522.  
  1523.     if (editType) {
  1524.         undo = GetUndoHndl(hndla);
  1525.         if (!mDerefUndo(undo)->disabled) {
  1526.             task = GetUndoTaskHndl(undo, editType);
  1527.             if (task) {
  1528.                 NewUndoPart(task, SWAP_CHILDREN, hndla, cnuma, hndlb, cnumb, false);
  1529.             }
  1530.         }
  1531.     }
  1532. }
  1533.  
  1534.  
  1535.  
  1536. /**********************************************************************/
  1537.  
  1538.  
  1539.  
  1540. static void    UndoNewChild(TreeObjHndl undoPart)
  1541. {
  1542.     TreeObjHndl    shndl, chndl;
  1543.     short        scnum;
  1544.  
  1545.     shndl = mDerefUndoPart(undoPart)->shndl;
  1546.     scnum = mDerefUndoPart(undoPart)->scnum;
  1547.     mDerefUndoPart(undoPart)->actionType = DISPOSE_CHILD;
  1548.  
  1549.     chndl = GetChildHndl(shndl, scnum);
  1550.     MoveChild(NO_EDIT, shndl, scnum, undoPart, 0);
  1551. }
  1552.  
  1553.  
  1554.  
  1555. /**********************************************************************/
  1556.  
  1557.  
  1558.  
  1559. static void    UndoDisposeChild(TreeObjHndl undoPart)
  1560. {
  1561.     TreeObjHndl    shndl, chndl;
  1562.     short        scnum;
  1563.  
  1564.     shndl = mDerefUndoPart(undoPart)->shndl;
  1565.     scnum = mDerefUndoPart(undoPart)->scnum;
  1566.     mDerefUndoPart(undoPart)->actionType = NEW_CHILD;
  1567.  
  1568.     chndl = GetChildHndl(undoPart, 0);
  1569.     MoveChild(NO_EDIT, undoPart, 0, shndl, scnum);
  1570. }
  1571.  
  1572.  
  1573.  
  1574. /**********************************************************************/
  1575.  
  1576.  
  1577.  
  1578. static void    UndoMoveChild(TreeObjHndl undoPart)
  1579. {
  1580.     TreeObjHndl    shndl, dhndl;
  1581.     short        scnum, dcnum;
  1582.  
  1583.     shndl = mDerefUndoPart(undoPart)->shndl;
  1584.     scnum = mDerefUndoPart(undoPart)->scnum;
  1585.     dhndl = mDerefUndoPart(undoPart)->dhndl;
  1586.     dcnum = mDerefUndoPart(undoPart)->dcnum;
  1587.  
  1588.     MoveChild(NO_EDIT, dhndl, dcnum, shndl, scnum);
  1589.  
  1590.     mDerefUndoPart(undoPart)->shndl = dhndl;
  1591.     mDerefUndoPart(undoPart)->scnum = dcnum;
  1592.     mDerefUndoPart(undoPart)->dhndl = shndl;
  1593.     mDerefUndoPart(undoPart)->dcnum = scnum;
  1594. }
  1595.  
  1596.  
  1597.  
  1598. /**********************************************************************/
  1599.  
  1600.  
  1601.  
  1602. static void    UndoModifyChild(TreeObjHndl undoPart)
  1603. {
  1604.     TreeObjHndl    shndl, dchndl, uchndl;
  1605.     short        scnum;
  1606.  
  1607.     shndl = mDerefUndoPart(undoPart)->shndl;
  1608.     scnum = mDerefUndoPart(undoPart)->scnum;
  1609.  
  1610.     dchndl = GetChildHndl(shndl, scnum);    /* Document child handle. */
  1611.     uchndl = GetChildHndl(undoPart, 0);        /* Undo child handle. */
  1612.  
  1613.     UndoModifyChildren(dchndl, uchndl, mDerefUndoPart(undoPart)->deepCopy);
  1614. }
  1615.  
  1616.  
  1617.  
  1618. /**********************************************************************/
  1619.  
  1620.  
  1621.  
  1622. static void    UndoModifyChildren(TreeObjHndl dhndl, TreeObjHndl uhndl, Boolean deepCopy)
  1623. {
  1624.     TreeObjHndl    dchndl, uchndl;
  1625.     short        i;
  1626.  
  1627.     DoTreeObjMethod(dhndl, UNDOMESSAGE, UNDOFROMDOC);    /* Old data leaving document. */
  1628.     SwapTreeObjData(dhndl, uhndl);
  1629.     DoTreeObjMethod(dhndl, UNDOMESSAGE, UNDOTODOC);        /* New data entering document. */
  1630.  
  1631.     if (deepCopy) {
  1632.         for (i = (*dhndl)->numChildren; i;) {
  1633.             dchndl = GetChildHndl(dhndl, --i);
  1634.             uchndl = GetChildHndl(uhndl, i);
  1635.             UndoModifyChildren(dchndl, uchndl, deepCopy);
  1636.         }
  1637.     }
  1638. }
  1639.  
  1640.  
  1641.  
  1642. /**********************************************************************/
  1643.  
  1644.  
  1645.  
  1646. static void    UndoSwapChildren(TreeObjHndl undoPart)
  1647. {
  1648.     TreeObjHndl    shndl, dhndl, schndl, dchndl;
  1649.     short        scnum, dcnum;
  1650.  
  1651.     shndl = mDerefUndoPart(undoPart)->shndl;
  1652.     scnum = mDerefUndoPart(undoPart)->scnum;
  1653.     dhndl = mDerefUndoPart(undoPart)->dhndl;
  1654.     dcnum = mDerefUndoPart(undoPart)->dcnum;
  1655.  
  1656.     schndl = GetChildHndl(shndl, scnum);
  1657.     dchndl = GetChildHndl(dhndl, dcnum);
  1658.  
  1659.     DoTreeObjMethod(schndl, UNDOMESSAGE, UNDOFROMDOC);
  1660.     DoTreeObjMethod(dchndl, UNDOMESSAGE, UNDOFROMDOC);
  1661.  
  1662.     SwapChildren(NO_EDIT, shndl, scnum, dhndl, dcnum);
  1663.  
  1664.     DoTreeObjMethod(schndl, UNDOMESSAGE, UNDOTODOC);
  1665.     DoTreeObjMethod(dchndl, UNDOMESSAGE, UNDOTODOC);
  1666. }
  1667.  
  1668.  
  1669.  
  1670. /**********************************************************************/
  1671.  
  1672.  
  1673.  
  1674. /* Dispose of all undo information and prevent further undo collection.
  1675. ** Calling NewUndo() will re-enable undo collection. */
  1676.  
  1677. void    DisableUndo(TreeObjHndl hndl)
  1678. {
  1679.     TreeObjHndl    undo;
  1680.  
  1681.     undo = GetUndoHndl(hndl);
  1682.     while ((*undo)->numChildren) DisposeChild(NO_EDIT, undo, 0);
  1683.  
  1684.     mDerefUndo(undo)->undoDepth = 0;
  1685.     mDerefUndo(undo)->disabled  = true;
  1686. }
  1687.  
  1688.  
  1689.  
  1690. /**********************************************************************/
  1691.  
  1692.  
  1693.  
  1694. /* Dispose of all undo information and prevent further undo collection.
  1695. ** Calling NewUndo() will re-enable undo collection. */
  1696.  
  1697. void    DisposeUndos(TreeObjHndl hndl)
  1698. {
  1699.     DisableUndo(hndl);
  1700.     NewUndo(hndl);
  1701. }
  1702.  
  1703.  
  1704.  
  1705. /**********************************************************************/
  1706.  
  1707.  
  1708.  
  1709. /* Dispose of all undo information except the current undo.  The current undo
  1710. ** may still be active, and it may be needed to back out of an edit operation. */
  1711.  
  1712. Boolean    PurgeUndo(TreeObjHndl hndl)
  1713. {
  1714.     TreeObjHndl    undo;
  1715.     Boolean        didPurge;
  1716.  
  1717.     undo = GetUndoHndl(hndl);
  1718.     didPurge = false;
  1719.     while ((*undo)->numChildren > 1) {
  1720.         DisposeChild(NO_EDIT, undo, 0);
  1721.         if (mDerefUndo(undo)->undoDepth) --mDerefUndo(undo)->undoDepth;
  1722.         didPurge = true;
  1723.     }
  1724.  
  1725.     return(didPurge);
  1726. }
  1727.  
  1728.  
  1729.  
  1730. /**********************************************************************/
  1731.  
  1732.  
  1733.  
  1734. void    GetUndoInfo(FileRecHndl frHndl, short *undoDepth, short *numUndos)
  1735. {
  1736.     TreeObjHndl    undo;
  1737.  
  1738.     *undoDepth = *numUndos = 0;
  1739.     if ((*frHndl)->fileState.readOnly) return;
  1740.  
  1741.     undo = GetUndoHndl((*frHndl)->d.doc.root);
  1742.     if (!mDerefUndo(undo)->disabled) {
  1743.         *undoDepth = mDerefUndo(undo)->undoDepth;
  1744.         *numUndos  = (*undo)->numChildren;
  1745.     }
  1746. }
  1747.  
  1748.  
  1749.  
  1750. /*****************************************************************************/
  1751. /*****************************************************************************/
  1752.  
  1753.  
  1754.  
  1755. /* This function does the standard document initialization. */
  1756.  
  1757. OSErr    DefaultInitDocument(FileRecHndl frHndl, short version, short numUndos, short numSaveUndos)
  1758. {
  1759.     TreeObjHndl    root, undo;
  1760.     OSErr        err;
  1761.  
  1762.     err = noErr;
  1763.  
  1764.     (*frHndl)->d.doc.fhInfo.version = version;
  1765.  
  1766.     (*frHndl)->fileState.readDocumentHeaderProc  = DefaultReadDocumentHeader;
  1767.     (*frHndl)->fileState.writeDocumentHeaderProc = DefaultWriteDocumentHeader;
  1768.  
  1769.     root = NewRootObj(ROOTOBJ, 0);
  1770.     if (root) {                                    /* Create hierarchical data root. */
  1771.         (*frHndl)->d.doc.root = root;            /* Link file to hierarchical data. */
  1772.         undo = NewRootObj(UNDOOBJ, 0);            /* Create hierarchical undo root. */
  1773.         mDerefRoot(root)->undo    = undo;        /* Save hierarchical undo root. */
  1774.         mDerefRoot(root)->frHndl  = frHndl;
  1775.         if (undo) {
  1776.             (*frHndl)->fileState.defaultDoc = true;
  1777.             mDerefUndo(undo)->root   = root;    /* Point undo back at file root. */
  1778.             mDerefUndo(undo)->frHndl = frHndl;
  1779.             mDerefUndo(undo)->maxNumUndos  = numUndos;
  1780.             mDerefUndo(undo)->numSaveUndos = numSaveUndos;
  1781.         }
  1782.         else {
  1783.             DefaultFreeDocument(frHndl);
  1784.             err = memFullErr;
  1785.         }
  1786.     }
  1787.     else err = memFullErr;
  1788.  
  1789.     return(err);
  1790. }
  1791.  
  1792.  
  1793.  
  1794. /*****************************************************************************/
  1795.  
  1796.  
  1797.  
  1798. /* Frees up the hierarchical document and undo portions of a default document. */
  1799.  
  1800. OSErr    DefaultFreeDocument(FileRecHndl frHndl)
  1801. {
  1802.     TreeObjHndl    root, undo;
  1803.  
  1804.     root = (*frHndl)->d.doc.root;
  1805.     if (root) {                                    /* If we have a valid root object... */
  1806.         undo = mDerefRoot(root)->undo;
  1807.         if (undo)                                /* If we have a valid undo object... */
  1808.             DisposeObjAndOffspring(undo);        /* Dispose of undo info. */
  1809.         DisposeObjAndOffspring(root);            /* Dispose of hierarchical file data. */
  1810.         (*frHndl)->d.doc.root = nil;
  1811.     }
  1812.     return(noErr);
  1813. }
  1814.  
  1815.  
  1816.  
  1817. /*****************************************************************************/
  1818.  
  1819.  
  1820.  
  1821. OSErr    DefaultReadDocument(FileRecHndl frHndl)
  1822. {
  1823.     OSErr        err;
  1824.     TreeObjHndl    root, undo;
  1825.     short        resID, refNum, flags;
  1826.     Movie        movie;
  1827.     Boolean        wasChanged;
  1828.  
  1829.     refNum = (*frHndl)->fileState.refNum;
  1830.     err    = SetFPos(refNum, fsFromStart, 0);
  1831.         /* Set the file position to the beginning of the file. */
  1832.  
  1833.     err = DoReadDocumentHeader(frHndl);
  1834.  
  1835.     if (!err) {
  1836.         if ((*frHndl)->fileState.sfType == MovieFileType) {
  1837.             resID  = 0;
  1838.             flags  = (*frHndl)->fileState.movieFlags;
  1839.             err = NewMovieFromFile(&movie, refNum, &resID, nil, flags, &wasChanged);
  1840.             if (err) return(err);
  1841.             (*frHndl)->fileState.movie                  = movie;
  1842.             (*frHndl)->fileState.movieResID             = resID;
  1843.             (*frHndl)->fileState.movieDataRefWasChanged = wasChanged;
  1844.         }
  1845.         else {
  1846.             root = (*frHndl)->d.doc.root;        /* Preserve the undo field.  Preserving  */
  1847.             undo = mDerefRoot(root)->undo;        /* any application-sepcific fields is up */
  1848.                                                 /* to the application.                     */
  1849.             err = ReadTree(root, refNum);
  1850.                 /* Read in the hierarchical file data portion. */
  1851.  
  1852.             mDerefRoot(root)->undo   = undo;    /* Restore the 2 over-written fields. */
  1853.             mDerefRoot(root)->frHndl = frHndl;
  1854.         }
  1855.     }
  1856.  
  1857.     return(err);
  1858. }
  1859.  
  1860.  
  1861.  
  1862. /*****************************************************************************/
  1863.  
  1864.  
  1865.  
  1866. OSErr    DefaultReadDocumentFixup(FileRecHndl frHndl)
  1867. {
  1868.     TreeObjHndl    root, undo, chndl;
  1869.  
  1870.     root = (*frHndl)->d.doc.root;
  1871.     undo = mDerefRoot(root)->undo;
  1872.     for (;;) {
  1873.         if (!(chndl = GetChildHndl(root, -1))) break;
  1874.         if ((*chndl)->type != UNDOTASKOBJ)     break;
  1875.         MoveChild(NO_EDIT, root, -1, undo, -1);
  1876.         mDerefUndo(undo)->undoDepth++;
  1877.             /* Move any undo tasks that were saved with the document
  1878.             ** out of the document and into the undo hierarchy. */
  1879.     }
  1880.  
  1881.     return(noErr);
  1882. }
  1883.  
  1884.  
  1885.  
  1886. /*****************************************************************************/
  1887.  
  1888.  
  1889.  
  1890. OSErr    DefaultWriteDocument(FileRecHndl frHndl)
  1891. {
  1892.     short        refNum, cnum, numSaveUndos;
  1893.     OSErr        err;
  1894.     long        fpos;
  1895.     TreeObjHndl    root, undo, chndl;
  1896.  
  1897.     refNum = (*frHndl)->fileState.refNum;
  1898.     err    = DoWriteDocumentHeader(frHndl);
  1899.  
  1900.     if (!err) {
  1901.         if ((*frHndl)->fileState.sfType != MovieFileType) {
  1902.             undo         = GetUndoHndl(root = (*frHndl)->d.doc.root);
  1903.             numSaveUndos = mDerefUndo(undo)->numSaveUndos;
  1904.             for (cnum = mDerefUndo(undo)->undoDepth; ((cnum) && (numSaveUndos)); --numSaveUndos)
  1905.                 MoveChild(NO_EDIT, undo, --cnum, root, -1);
  1906.                     /* Move the designated number of undo tasks into the document side.
  1907.                     ** They will be saved to disk this way.  The designated number may be
  1908.                     ** zero, which means that undos aren't saved to disk. */
  1909.  
  1910.             DoNumberTree(root);
  1911.                 /* Assign each object in the tree a unique treeID.  This will allow
  1912.                 ** the objects to convert handle references into ID references so that
  1913.                 ** the data can be saved to disk. */
  1914.  
  1915.             err = WriteTree(root, refNum);
  1916.                 /* Write out the hierarchical data.  This includes the data in the root object.
  1917.                 ** When reading a data file, the root object data has already been initialized,
  1918.                 ** and therefore reading in the old root data from disk is a bad thing.  This is
  1919.                 ** handled by caching the root data prior to reading in a file, and then
  1920.                 ** restoring the data after the ReadTree() call has been made. */
  1921.  
  1922.             for (;;) {
  1923.                 if (!(chndl = GetChildHndl(root, -1))) break;
  1924.                 if ((*chndl)->type != UNDOTASKOBJ)     break;
  1925.                 MoveChild(NO_EDIT, root, -1, undo, cnum++);
  1926.                     /* Move any undo tasks that we previously moved into the document
  1927.                     ** out of the document and back into the undo hierarchy. */
  1928.             }
  1929.  
  1930.             if (!err) {
  1931.                 err = GetFPos(refNum, &fpos);
  1932.                 if (!err)
  1933.                     err = SetEOF(refNum, fpos);
  1934.             }            /* The document may be shorter than last time it was written to disk.
  1935.                         ** Handle this case by ending the file based on the new length. */
  1936.         }
  1937.         else {
  1938.         }
  1939.     }
  1940.  
  1941.     return(err);
  1942. }
  1943.  
  1944.  
  1945.  
  1946. /**********************************************************************/
  1947. /**********************************************************************/
  1948. /**********************************************************************/
  1949.  
  1950.  
  1951.  
  1952. OSErr    HReadTree(TreeObjHndl hndl, Handle tree)
  1953. {
  1954.     OSErr    err;
  1955.  
  1956.     if (!GetHandleSize(tree)) return(noErr);
  1957.  
  1958.     err = HReadBranch(hndl, tree);
  1959.     if (err) {
  1960.         while ((*hndl)->numChildren)
  1961.             DisposeChild(NO_EDIT, hndl, 0);
  1962.     }
  1963.     else {
  1964.         DoNumberTree(hndl);
  1965.         DoFTreeMethod(hndl, CONVERTMESSAGE, CONVERTTOHNDL);
  1966.     }
  1967.  
  1968.     return(err);
  1969. }
  1970.  
  1971.  
  1972.  
  1973. /**********************************************************************/
  1974.  
  1975.  
  1976.  
  1977. /* This function recursively dissects the handle into separate tree objects.  The handle
  1978. ** has previously had tree objects streamed into it.  The public format is as follows:
  1979. ** 1) object header
  1980. ** 2) data length (4 bytes)
  1981. ** 3) data
  1982. **
  1983. ** After the object is created, the header is moved into it.  Then the data length is
  1984. ** fetched and a data handle is created to hold the data portion of the streamed data
  1985. ** for this object.  Once the data handle holds the object data, the tree handle has
  1986. ** the information for this object removed from it.
  1987. ** After this data separation, we call the object and pass it the data handle.
  1988. ** The object is responsible for interpreting the handle and initializing the data
  1989. ** portion of the object with it. */
  1990.  
  1991. OSErr    HReadBranch(TreeObjHndl hndl, Handle tree)
  1992. {
  1993.     TreeObjHndl        chndl;
  1994.     Handle            treeObjData;
  1995.     TreeObjProcPtr    proc;
  1996.     short            numChildren, cnum;
  1997.     long            size, diff;
  1998.     OSErr            err;
  1999.  
  2000.     if (GetHandleSize(tree) < sizeof(TreeObj)) return(paramErr);
  2001.  
  2002.     BlockMove(*tree, *hndl, sizeof(TreeObj) - sizeof(TreeObjHndl));
  2003.  
  2004.     numChildren = (*hndl)->numChildren;    /* So we can dispose of a partial read if */
  2005.     (*hndl)->numChildren = 0;            /* we have a failure after this point.    */
  2006.  
  2007.     BlockMove((long *)(*tree + sizeof(TreeObj) - sizeof(TreeObjHndl)), (Ptr)&size, sizeof(size));
  2008.         /* Get the size of the data following the header data. */
  2009.  
  2010.     if (!(treeObjData = NewHandle(size)))
  2011.         return(memFullErr);
  2012.             /* Create a handle the size of the data. */
  2013.  
  2014.     BlockMove(*tree + sizeof(TreeObj), *treeObjData, size);
  2015.         /* Copy the data into the handle. */
  2016.  
  2017.     diff = sizeof(TreeObj) + size;
  2018.     size = GetHandleSize(tree) - diff;
  2019.         /* Calculate how much we are going to shrink the tree handle. */
  2020.  
  2021.     BlockMove(*tree + diff, *tree, size);
  2022.     SetHandleSize(tree, size);
  2023.  
  2024.     proc = gTreeObjMethods[(*hndl)->type];
  2025.     if (proc)
  2026.         err = (*proc)(hndl, HREADMESSAGE, (long)treeObjData);
  2027.     else
  2028.         err = HReadTreeObjData(hndl, treeObjData);
  2029.  
  2030.     DisposeHandle(treeObjData);
  2031.  
  2032.     if (err)
  2033.         return(err);
  2034.  
  2035.     for (cnum = 0; cnum < numChildren; ++cnum) {
  2036.         if (!(chndl = NewRootObj(EMPTYOBJ, 0))) return(memFullErr);
  2037.         if (InsertChildHndl(hndl, chndl, cnum)) {
  2038.             DisposeObjAndOffspring(chndl);
  2039.             return(memFullErr);
  2040.         }
  2041.         err = HReadBranch(chndl, tree);
  2042.         if (err) return(err);
  2043.     }
  2044.  
  2045.     return(noErr);
  2046. }
  2047.  
  2048.  
  2049.  
  2050. /**********************************************************************/
  2051.  
  2052.  
  2053.  
  2054. /* The simple handle read can assume that there will be no expansion or interpretation
  2055. ** of the data.  This means that the dataSize field represents the correct data size. */
  2056.  
  2057. OSErr    HReadTreeObjData(TreeObjHndl hndl, Handle treeObjData)
  2058. {
  2059.     long    dataSize;
  2060.     OSErr    err;
  2061.  
  2062.     if (!(err = SetDataSize(hndl, dataSize = (*hndl)->dataSize)))
  2063.         BlockMove(*treeObjData, (Ptr)(*hndl + 1), dataSize);
  2064.  
  2065.     return(err);
  2066. }
  2067.  
  2068.  
  2069.  
  2070. /**********************************************************************/
  2071.  
  2072.  
  2073.  
  2074. /* Given a handle, this function is called to stream the hierarchy onto the end of the handle. */
  2075.  
  2076. OSErr    HWriteTree(TreeObjHndl hndl, Handle tree)
  2077. {
  2078.     TreeObjProcPtr    proc;
  2079.     Handle            data;
  2080.     short            cnum;
  2081.     OSErr            err;
  2082.     long            tsize, dsize;
  2083.  
  2084.     DoTreeObjMethod(hndl, CONVERTMESSAGE, CONVERTTOID);
  2085.         /* Ready data to be written to file.  Any references to handles are invalid
  2086.         ** when written to disk.  These need to be converted to a reference that
  2087.         ** makes sense when read in from disk when a file is opened.  The standard
  2088.         ** way to do this is to convert the handle reference to a tree-obj-number
  2089.         ** reference.  Prior to WriteTree() being called, DoNumberTree() is called.
  2090.         ** DoNumberTree() walks the tree and numbers each handle sequentially, thus
  2091.         ** giving each handle a unique id number. */
  2092.  
  2093.     if (!(data = NewHandle((*hndl)->dataSize)))
  2094.         return(memFullErr);
  2095.  
  2096.     proc = gTreeObjMethods[(*hndl)->type];
  2097.     if (proc)
  2098.         err = (*proc)(hndl, HWRITEMESSAGE, (long)data);
  2099.     else
  2100.         err = HWriteTreeObjData(hndl, data);
  2101.  
  2102.     if (err) {
  2103.         DisposeHandle(data);
  2104.         return(err);
  2105.     }
  2106.  
  2107.     tsize = GetHandleSize(tree);
  2108.     dsize = GetHandleSize(data);
  2109.     SetHandleSize(tree, tsize + sizeof(TreeObj) + dsize);
  2110.     BlockMove((Ptr)*hndl,  (*tree + tsize), sizeof(TreeObj) - sizeof(TreeObjHndl));
  2111.     BlockMove((Ptr)&dsize, (*tree + tsize + sizeof(TreeObj) - sizeof(TreeObjHndl)), sizeof(long));
  2112.     BlockMove(*data,       (*tree + tsize + sizeof(TreeObj)), dsize);
  2113.  
  2114.     DisposeHandle(data);
  2115.     DoTreeObjMethod(hndl, CONVERTMESSAGE, CONVERTTOHNDL);
  2116.         /* Undo any id references back to handle references. */
  2117.  
  2118.     for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum) {
  2119.         err = HWriteTree(GetChildHndl(hndl, cnum), tree);
  2120.         if (err) break;
  2121.     }
  2122.  
  2123.     return(err);
  2124. }
  2125.  
  2126.  
  2127.  
  2128. /**********************************************************************/
  2129.  
  2130.  
  2131.  
  2132. /* Write out dataSize number of bytes from the object onto end of handle. */
  2133.  
  2134. OSErr    HWriteTreeObjData(TreeObjHndl hndl, Handle data)
  2135. {
  2136.     long    dataSize;
  2137.     OSErr    err;
  2138.  
  2139.     SetHandleSize(data, dataSize = (*hndl)->dataSize);
  2140.     err = MemError();
  2141.     if (err) return(err);
  2142.  
  2143.     BlockMove((Ptr)(*hndl + 1), *data, dataSize);
  2144.     return(noErr);
  2145. }
  2146.  
  2147.  
  2148.  
  2149. /**********************************************************************/
  2150. /**********************************************************************/
  2151.  
  2152.  
  2153.  
  2154. long    GetCData(TreeObjHndl hndl, long offset, char *data)
  2155. {
  2156.     Ptr        dptr;
  2157.     long    len;
  2158.  
  2159.     dptr = (Ptr)GetDataPtr(hndl) + offset;
  2160.     len  = clen(dptr);
  2161.  
  2162.     if (data)
  2163.         ccpy(data, dptr);
  2164.  
  2165.     return(len);
  2166. }
  2167.  
  2168.  
  2169.  
  2170. /**********************************************************************/
  2171.  
  2172.  
  2173.  
  2174. OSErr    PutCData(TreeObjHndl hndl, long offset, char *data)
  2175. {
  2176.     unsigned char    *dptr;
  2177.     long            oldl, newl;
  2178.     OSErr            err;
  2179.  
  2180.     dptr = (unsigned char *)GetDataPtr(hndl) + offset;
  2181.  
  2182.     oldl = clen((Ptr)dptr) + 1;
  2183.     newl = clen(data) + 1;
  2184.  
  2185.     err = SlideData(hndl, offset + oldl, newl - oldl);
  2186.     if (err) return(err);
  2187.  
  2188.     dptr = (unsigned char *)GetDataPtr(hndl) + offset;
  2189.     ccpy((Ptr)dptr, data);
  2190.  
  2191.     return(noErr);
  2192. }
  2193.  
  2194.  
  2195.  
  2196. /**********************************************************************/
  2197.  
  2198.  
  2199.  
  2200. void    GetPData(TreeObjHndl hndl, long offset, StringPtr data)
  2201. {
  2202.     StringPtr    dptr;
  2203.  
  2204.     dptr = (StringPtr)GetDataPtr(hndl) + offset;
  2205.     pcpy(data, dptr);
  2206. }
  2207.  
  2208.  
  2209.  
  2210. /**********************************************************************/
  2211.  
  2212.  
  2213.  
  2214. OSErr    PutPData(TreeObjHndl hndl, long offset, StringPtr data)
  2215. {
  2216.     unsigned char    *dptr;
  2217.     long            oldl, newl;
  2218.     OSErr            err;
  2219.  
  2220.     dptr = (unsigned char *)GetDataPtr(hndl) + offset;
  2221.  
  2222.     oldl = *dptr + 1;
  2223.     newl = (*(unsigned char *)data) + 1;
  2224.  
  2225.     err = SlideData(hndl, offset + oldl, newl - oldl);
  2226.     if (err) return(err);
  2227.  
  2228.     dptr = (unsigned char *)GetDataPtr(hndl) + offset;
  2229.     pcpy(dptr, data);
  2230.  
  2231.     return(noErr);
  2232. }
  2233.  
  2234.  
  2235.  
  2236. /**********************************************************************/
  2237.  
  2238.  
  2239.  
  2240. OSErr    PutShortData(TreeObjHndl hndl, long offset, void *data, unsigned short size)
  2241. {
  2242.     unsigned short    *dptr;
  2243.     long            oldl, newl;
  2244.     short            ss;
  2245.     OSErr            err;
  2246.  
  2247.     dptr = (unsigned short *)((unsigned char *)GetDataPtr(hndl) + offset);
  2248.  
  2249.     BlockMove(dptr, &ss, sizeof(short));
  2250.     oldl = ss    + sizeof(short);
  2251.     newl = size  + sizeof(short);
  2252.  
  2253.     err = SlideData(hndl, offset + oldl, newl - oldl);
  2254.     if (err) return(err);
  2255.  
  2256.     dptr = (unsigned short *)((unsigned char *)GetDataPtr(hndl) + offset);
  2257.     BlockMove(&size, dptr, sizeof(short));
  2258.     BlockMove(data, dptr + 1, size);
  2259.  
  2260.     return(noErr);
  2261. }
  2262.  
  2263.  
  2264.  
  2265. /**********************************************************************/
  2266.  
  2267.  
  2268.  
  2269. OSErr    PutLongData(TreeObjHndl hndl, long offset, void *data, long size)
  2270. {
  2271.     unsigned long    *dptr, ll;
  2272.     long            oldl, newl;
  2273.     OSErr            err;
  2274.  
  2275.     dptr = (unsigned long *)((unsigned char *)GetDataPtr(hndl) + offset);
  2276.  
  2277.     BlockMove(dptr, &ll, sizeof(long));
  2278.     oldl = ll    + sizeof(long);
  2279.     newl = size  + sizeof(long);
  2280.  
  2281.     err = SlideData(hndl, offset + oldl, newl - oldl);
  2282.     if (err) return(err);
  2283.  
  2284.     dptr = (unsigned long *)((unsigned char *)GetDataPtr(hndl) + offset);
  2285.     BlockMove(&size, dptr, sizeof(long));
  2286.     BlockMove(data, dptr + 1, size);
  2287.  
  2288.     return(noErr);
  2289. }
  2290.  
  2291.  
  2292.  
  2293. /**********************************************************************/
  2294.  
  2295.  
  2296.  
  2297. unsigned long    GetDataOffset(TreeObjHndl hndl, unsigned long offset, short dtype, short dnum)
  2298. {
  2299.     unsigned char    *dptr;
  2300.     short            ss;
  2301.     long            ll;
  2302.  
  2303.     while (dnum--) {
  2304.     dptr = (unsigned char *)GetDataPtr(hndl) + offset;
  2305.         switch (dtype) {
  2306.             case kCStr:
  2307.                 offset += clen((Ptr)dptr) + 1;
  2308.                 break;
  2309.             case kPStr:
  2310.                 offset += *dptr + 1;
  2311.                 break;
  2312.             case kSDataBlock:
  2313.                 BlockMove(dptr, &ss, sizeof(short));
  2314.                 offset += ss + sizeof(short);
  2315.                 break;
  2316.             case kLDataBlock:
  2317.                 BlockMove(dptr, &ll, sizeof(long));
  2318.                 offset += ll + sizeof(long);
  2319.                 break;
  2320.         }
  2321.     }
  2322.  
  2323.     return(offset);
  2324. }
  2325.  
  2326.  
  2327.  
  2328. /**********************************************************************/
  2329. /**********************************************************************/
  2330.  
  2331.  
  2332.  
  2333. Boolean    EqualTreeObjData(TreeObjHndl h1, TreeObjHndl h2)
  2334. {
  2335.     TreeObjProcPtr    proc;
  2336.  
  2337.     if ((h1) && (!h2)) return(false);
  2338.     if ((h2) && (!h1)) return(false);
  2339.  
  2340.     if ((*h1)->type != (*h2)->type) return(false);
  2341.  
  2342.     proc = gTreeObjMethods[(*h1)->type];
  2343.     if (proc)
  2344.         return((*proc)(h1, COMPAREMESSAGE, (long)h2));
  2345.     else
  2346.         return(DefaultEqualTreeObjData(h1, h2));
  2347. }
  2348.  
  2349.  
  2350.  
  2351. /**********************************************************************/
  2352.  
  2353.  
  2354.  
  2355. Boolean    DefaultEqualTreeObjData(TreeObjHndl h1, TreeObjHndl h2)
  2356. {
  2357.     Ptr        p1, p2;
  2358.     long    ii, jj;
  2359.  
  2360.     if ((jj = (*h1)->dataSize) != (*h2)->dataSize) return(false);
  2361.  
  2362.     p1 = GetDataPtr(h1);
  2363.     p2 = GetDataPtr(h2);
  2364.  
  2365.     for (ii = 0; ii < jj; ++ii) {
  2366.         if (p1[ii] != p2[ii]) return(false);
  2367.     }
  2368.  
  2369.     return(true);
  2370. }
  2371.  
  2372.  
  2373.  
  2374.